#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Globales.h"
#include "TSintactico.h"
#include "TPila.h"

#define ERROR_SINTAXIS 402

int TSintactico_Crear(TSintactico* as) {
  int iError = NO_HAY_ERRORES;
  as->m_iUltimoError = NO_HAY_ERRORES;
  strcpyMem(&(as->m_szUltimoError), "");

  TPila_Crear(&(as->m_xTokensEspeciales), sizeof(Token));

  as->m_ettUltimoToken = TOKEN_VACIO;
  as->m_iNivelJerarquiaObjetos = 0;

  return iError;
}

int TSintactico_Destruir(TSintactico* al) {
  int iError = NO_HAY_ERRORES;
  strcpyMem(&(al->m_szUltimoError), "");
  return iError;
}

eBoolean febAceptaToken(TSintactico* as, eTipoToken ettTokenRecibido, eTipoToken ettAnterior) {
  eBoolean bTokenValido;
  int i = 0;
  Token ettUltimoTokenEspecial;

  eTipoToken aettString[4] = {TOKEN_DOSPUNTOS, TOKEN_COMA, TOKEN_OBJETO_EMPIEZA, TOKEN_ARRAY_EMPIEZA};
  eTipoToken aettNumero[4] = {TOKEN_COMA, TOKEN_DOSPUNTOS, TOKEN_OBJETO_EMPIEZA, TOKEN_ARRAY_EMPIEZA};
  eTipoToken aettObjetoEmpieza[5] = {TOKEN_COMA, TOKEN_OBJETO_EMPIEZA, TOKEN_OBJETO_TERMINA, TOKEN_ARRAY_EMPIEZA, TOKEN_DOSPUNTOS};
  eTipoToken aettObjetoTermina[8] = {TOKEN_STRING, TOKEN_NUMERO, TOKEN_ARRAY_TERMINA, TOKEN_FALSE, TOKEN_NULL, TOKEN_TRUE, TOKEN_OBJETO_TERMINA, TOKEN_OBJETO_EMPIEZA};
  eTipoToken aettArrayEmpieza[5] = {TOKEN_COMA, TOKEN_OBJETO_EMPIEZA, TOKEN_OBJETO_TERMINA, TOKEN_DOSPUNTOS, TOKEN_ARRAY_EMPIEZA};
  eTipoToken aettArrayTermina[8] = {TOKEN_STRING, TOKEN_NUMERO, TOKEN_ARRAY_TERMINA, TOKEN_FALSE, TOKEN_NULL, TOKEN_TRUE, TOKEN_OBJETO_TERMINA, TOKEN_ARRAY_EMPIEZA};
  eTipoToken aettComa[7] = {TOKEN_STRING, TOKEN_ARRAY_TERMINA, TOKEN_FALSE, TOKEN_NULL, TOKEN_TRUE, TOKEN_NUMERO, TOKEN_OBJETO_TERMINA};
  eTipoToken aettDosPuntos[1] = {TOKEN_STRING};
  eTipoToken aettTrue[3] = {TOKEN_DOSPUNTOS, TOKEN_ARRAY_EMPIEZA, TOKEN_COMA};
  eTipoToken aettFalse[3] = {TOKEN_DOSPUNTOS, TOKEN_ARRAY_EMPIEZA, TOKEN_COMA};
  eTipoToken aettNull[3] = {TOKEN_DOSPUNTOS, TOKEN_ARRAY_EMPIEZA, TOKEN_COMA};
  bTokenValido = ebFalse;
  if (ettAnterior != TOKEN_VACIO) {
    switch (ettTokenRecibido) {
      case TOKEN_STRING:         for(i = 0; i < 4; i++) if (ettAnterior == aettString[i]) bTokenValido = ebTrue; break;
      case TOKEN_NUMERO:         for(i = 0; i < 4; i++) if (ettAnterior == aettNumero[i]) bTokenValido = ebTrue; break;
      case TOKEN_OBJETO_EMPIEZA: for(i = 0; i < 5; i++) if (ettAnterior == aettObjetoEmpieza[i]) bTokenValido = ebTrue; break;
      case TOKEN_OBJETO_TERMINA:
        TPila_Sacar(&(as->m_xTokensEspeciales), &ettUltimoTokenEspecial);
        if (ettUltimoTokenEspecial.m_ettTipo == TOKEN_OBJETO_EMPIEZA)
          for(i = 0; i < 8; i++) if (ettAnterior == aettObjetoTermina[i]) bTokenValido = ebTrue;
        break;
      case TOKEN_ARRAY_EMPIEZA: for(i = 0; i < 5; i++) if (ettAnterior == aettArrayEmpieza[i]) bTokenValido = ebTrue; break;
      case TOKEN_ARRAY_TERMINA:
        TPila_Sacar(&(as->m_xTokensEspeciales), &ettUltimoTokenEspecial);
        if (ettUltimoTokenEspecial.m_ettTipo == TOKEN_ARRAY_EMPIEZA)
          for(i = 0; i < 8; i++) if (ettAnterior == aettArrayTermina[i]) bTokenValido = ebTrue; break;
      case TOKEN_COMA:           for(i = 0; i < 7; i++) if (ettAnterior == aettComa[i]) bTokenValido = ebTrue; break;
      case TOKEN_DOSPUNTOS:      for(i = 0; i < 1; i++) if (ettAnterior == aettDosPuntos[i]) bTokenValido = ebTrue; break;
      case TOKEN_TRUE:           for(i = 0; i < 3; i++) if (ettAnterior == aettTrue[i]) bTokenValido = ebTrue; break;
      case TOKEN_FALSE:          for(i = 0; i < 3; i++) if (ettAnterior == aettFalse[i]) bTokenValido = ebTrue; break;
      case TOKEN_NULL:           for(i = 0; i < 3; i++) if (ettAnterior == aettNull[i]) bTokenValido = ebTrue; break;
	  default:
	    break;
    }
 } else {
   bTokenValido = ebTrue;
 }
 return bTokenValido;
}

int TSintactico_PushToken(TSintactico* as, Token* token) {
  int iError = NO_HAY_ERRORES;
  eBoolean ebAceptaToken = ebFalse;
  eBoolean ebModificoLista = ebFalse;

  ebAceptaToken = febAceptaToken(as, token->m_ettTipo, as->m_ettUltimoToken);
  if (ebAceptaToken == ebFalse) {
    iError = ERROR_SINTAXIS;
    as->m_iUltimoError = ERROR_SINTAXIS;
    strcpyMem(&(as->m_szUltimoError), "ERROR DE SINTAXIS");
  } else {
    switch (token->m_ettTipo) {
      case TOKEN_OBJETO_EMPIEZA:
        if (as->m_iNivelJerarquiaObjetos == 0) {
          iError = as->callback(as->m_pTDAContexto, CB_COMIENZA_OBJETO, NULL);
        }
        ebModificoLista = TPila_Agregar(&(as->m_xTokensEspeciales), token);

        if (ebModificoLista != ebFalse) {
          as->m_iNivelJerarquiaObjetos = as->m_iNivelJerarquiaObjetos + 1;
        }
        break;
      case TOKEN_OBJETO_TERMINA:
        if (as->m_iNivelJerarquiaObjetos > 1) {
          iError = as->callback(as->m_pTDAContexto, CB_TERMINA_OBJETO, NULL);
        }
        as->m_iNivelJerarquiaObjetos = as->m_iNivelJerarquiaObjetos - 1;
        break;
      case TOKEN_DOSPUNTOS:
        break;
      case TOKEN_COMA:
        break;
      case TOKEN_STRING:
          if (as->m_ettUltimoToken != TOKEN_DOSPUNTOS) {
            iError = as->callback(as->m_pTDAContexto, CB_CLAVE, token->m_szDato);
          } else {
            iError = as->callback(as->m_pTDAContexto, CB_STRING, token->m_szDato);
          }
        break;
      case TOKEN_NUMERO:
        iError = as->callback(as->m_pTDAContexto, CB_NUMERO, token->m_szDato);
        break;
      case TOKEN_ARRAY_EMPIEZA:
        iError = as->callback(as->m_pTDAContexto, CB_COMIENZA_ARRAY, NULL);
        TPila_Agregar(&(as->m_xTokensEspeciales), token);
        break;
      case TOKEN_ARRAY_TERMINA:
        iError = as->callback(as->m_pTDAContexto, CB_TERMINA_ARRAY, NULL);
        break;
      case TOKEN_TRUE:
        iError = as->callback(as->m_pTDAContexto, CB_TRUE, NULL);
        break;
      case TOKEN_FALSE:
        iError = as->callback(as->m_pTDAContexto, CB_FALSE, NULL);
        break;
      case TOKEN_NULL:
        iError = as->callback(as->m_pTDAContexto, CB_NULL, NULL);
        break;
	    default:
	      break;
    }
    as->m_ettUltimoToken = token->m_ettTipo;
  }
  return iError;
}

int TSintactico_terminarFlujo(TSintactico* as){
  int iError = NO_HAY_ERRORES;
  return iError;
}

int TSintactico_getUltimoError(TSintactico* as, int* codigo, char* mensaje){
  int iError = NO_HAY_ERRORES;
  *codigo = as->m_iUltimoError;
  strcpyMem(&mensaje, as->m_szUltimoError);
  return iError;
}

int TSintactico_setCallback(TSintactico* as, int evento, void* tda_contexto, int (*callback)(const void*, int, const void*)) {
  int iError = NO_HAY_ERRORES;
  as->callback = callback;
  as->m_pTDAContexto = tda_contexto;
  return iError;
}
